/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package de.adorsys.ohos.securestoragelibrary;

import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

import javax.crypto.Cipher;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * RSAUtils RSA非对称加密密钥对生成工具类,
 *
 * @since 2021-05-13
 */
public class RsaUtils {
    private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x000110, "RSAUtils");
    /** 算法名称 */
    private static final String ALGORITHM = "RSA";

    /** 密钥长度 */
    private static final int KEY_SIZE = 2048;

    private static RsaUtils instance = null;

    private RsaUtils() {
    }

    /**
     * 静态锁对象
     *
     * @return Utils
     */
    public static synchronized RsaUtils getInstance() {
        if (instance == null) {
            instance = new RsaUtils();
        }
        return instance;
    }

    /**
     * 随机生成密钥对（包含公钥和私钥）
     *
     * @return KeyPair
     * @throws Exception
     */
    public static KeyPair generateKeyPair() throws Exception {
        // 获取指定算法的密钥对生成器
        KeyPairGenerator gen = KeyPairGenerator.getInstance(ALGORITHM);

        // 初始化密钥对生成器（指定密钥长度, 使用默认的安全随机数源）
        gen.initialize(KEY_SIZE);

        // 随机生成一对密钥（包含公钥和私钥）
        return gen.generateKeyPair();
    }

    /**
     * 将 公钥/私钥 编码后以 Base64 的格式保存到指定文件
     *
     * @param key
     * @param keyFile
     * @throws IOException
     */
    public static void saveKeyForEncodedBase64(Key key, File keyFile) {
        try {
            // 获取密钥编码后的格式
            byte[] encBytes = key.getEncoded();

            // 转换为 Base64 文本
            String encBase64 = Base64.getEncoder().encodeToString(encBytes);
            IoUtils.writeFile(encBase64, keyFile);
        } catch (FileNotFoundException e) {
            HiLog.error(LABEL, e.getMessage());
        } catch (IOException e) {
            HiLog.error(LABEL, e.getMessage());
        }
    }

    /**
     * 根据公钥的 Base64 文本创建公钥对象
     *
     * @param pubKeyBase64
     * @return PublicKey
     * @throws Exception
     */
    public static PublicKey getPublicKey(String pubKeyBase64) throws Exception {
        // 把 公钥的Base64文本 转换为已编码的 公钥bytes
        byte[] encPubKey = Base64.getDecoder().decode(pubKeyBase64);

        // 创建 已编码的公钥规格
        X509EncodedKeySpec encPubKeySpec = new X509EncodedKeySpec(encPubKey);

        // 获取指定算法的密钥工厂, 根据 已编码的公钥规格, 生成公钥对象
        return KeyFactory.getInstance(ALGORITHM).generatePublic(encPubKeySpec);
    }

    /**
     * 根据私钥的 Base64 文本创建私钥对象
     *
     * @param priKeyBase64
     * @return PrivateKey
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String priKeyBase64) throws Exception {
        // 把 私钥的Base64文本 转换为已编码的 私钥bytes
        byte[] encPriKey = Base64.getDecoder().decode(priKeyBase64);

        // 创建 已编码的私钥规格
        PKCS8EncodedKeySpec encPriKeySpec = new PKCS8EncodedKeySpec(encPriKey);

        // 获取指定算法的密钥工厂, 根据 已编码的私钥规格, 生成私钥对象
        return KeyFactory.getInstance(ALGORITHM).generatePrivate(encPriKeySpec);
    }

    /**
     * 公钥加密数据
     *
     * @param plainData
     * @param pubKey
     * @return byte[]
     * @throws Exception
     */
    public static byte[] encrypt(byte[] plainData, PublicKey pubKey) throws Exception {
        // 获取指定算法的密码器
        Cipher cipher = Cipher.getInstance(ALGORITHM);

        // 初始化密码器（公钥加密模型）
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);

        // 加密数据, 返回加密后的密文
        return cipher.doFinal(plainData);
    }

    /**
     * 私钥解密数据
     *
     * @param cipherData
     * @param priKey
     * @return byte[]
     * @throws Exception
     */
    public static byte[] decrypt(byte[] cipherData, PrivateKey priKey) throws Exception {
        // 获取指定算法的密码器
        Cipher cipher = Cipher.getInstance(ALGORITHM);

        // 初始化密码器（私钥解密模型）
        cipher.init(Cipher.DECRYPT_MODE, priKey);

        // 解密数据, 返回解密后的明文
        return cipher.doFinal(cipherData);
    }
}
